{***************************************************************
    SpyGestion  Copyright (C) 2008 Christophe KOALI <nature2gas@gmail.com>
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
****************************************************************}
unit modInteractionKey;

interface

uses Classes, SysUtils, Windows, modInteractionAuteur;

const
    CNT_RSA_PUBLICKEY_VERSION : string = '1';


    CNT_ACCESS_LEVEL_NO_TIME_LIMIT =  1;
    CNT_ACCESS_LEVEL_OFFICIER      =  3;
    CNT_ACCESS_LEVEL_PROGAME       = 10;


{$IFDEF AUTH_FILE_USE}
type
    TLicenceKeyChecking = class(TObject)
    private
        FIsValidFile: boolean;

        FHeader: TFichierLicenceHeader;
        FDataCrypted: string;

        FHash : integer;

        /////////////////////////////////
        ///  Données du fichier crypté avec ma clé privé:
        FAccessLevel: integer;
        FForumUser: string;
        FForumPass: string;
        FPseudo: string;
        FDateGen: int64;

        FCaption: string;
        FAPropos: string;


        procedure LoadPublicKey(Key, Valeur: string);
        procedure LoadPublicFileFromBuffer(Tampon: string);


    public
        property formCaption: string read FCaption;
        property formAPropos: string read FAPropos;
        property AccessLevel: integer read FAccessLevel;

        property IsValidFile: boolean read FIsValidFile;
        property hash: integer read FHash;

        procedure LoadFromFile(Fichier: string);
        procedure LoadFromStream(FIn: TStream);

        procedure DecryptData(bLoadBuffer: boolean);

        constructor Create;
    end;
{$ENDIF}


procedure RSA_LoadPublicKey;
procedure RSA_UnLoadPublicKey;

function RSA_GetPublicKey: TStream;


// Ressource de la clé publique
{$R contact.RES}

// Nom de la ressource
const PUBLIC_KEY_RESSOURCE_NAME = 'omagik';



var
    RSA_PublicKey: TResourceStream;


implementation

uses modMd5, ZLibEx, sFonctionsChaines;


{$IFDEF AUTH_FILE_USE}
constructor TLicenceKeyChecking.Create;
begin
    inherited Create;

    FIsValidFile := false;

        FAccessLevel    := 0;
        FForumUser := '';
        FForumPass := '';
        FPseudo    := '';
        FDateGen   := 0;

        FCaption   := '';
        FAPropos   := '';
end;

procedure TLicenceKeyChecking.LoadPublicKey(Key, Valeur: string);
begin

    if SameText(Key, 'caption') then begin
       FCaption := Valeur;

    end else if SameText(Key, 'apropos') then begin
       FAPropos := Valeur;

    end else if SameText(Key, 'access') then begin
       FAccessLevel := StrToIntDef(Valeur, 0);

    end;

(*
pseudo=
forum_user=
forum_pass=
access=3
date_gen=1178165941

caption=
apropos=
*)

end;

procedure TLicenceKeyChecking.LoadPublicFileFromBuffer(Tampon: string);
var Ligne, Key: string;
    xPos: integer;
begin


    Tampon := trim(sfcRemplace(Tampon, #$0D, ''));

    while Tampon <> '' do begin

        xPos := pos(#$0A, Tampon);
        if xPos = 0 then begin
           Ligne  := Tampon;
           Tampon := '';
        end else begin
           Ligne := copy(Tampon, 1, xPos - 1);

           
           FillChar(Tampon[1], xPos, 0);
           System.Delete(Tampon, 1, xPos);
        end;

        Ligne := trim(Ligne);
        Tampon := trimleft(Tampon);

        if Ligne = '' then Continue;
        if Ligne[1] = '#' then Continue;
        if Ligne[1] = '/' then Continue;
        if Ligne[1] = ';' then Continue;

      
        xPos := pos('=', Ligne);
        if xPos > 0 then begin
           Key := copy(Ligne, 1, xPos - 1);
           System.Delete(Ligne, 1, xPos);

           LoadPublicKey(Key, Ligne);
        end;

    end;

    
end;


procedure TLicenceKeyChecking.DecryptData(bLoadBuffer: boolean);
var DecryptedDataBuffer: string;
begin
   DecryptedDataBuffer :=  iaRSADecryptageKeyStream(FDataCrypted, RSA_GetPublicKey, CNT_PUBLICKEY_PASSPHRASE);

   if length(FDataCrypted) < 10 then begin
      FIsValidFile := false;
   end else begin
      if bLoadBuffer then begin
         LoadPublicFileFromBuffer(DecryptedDataBuffer);

      end else begin
         FillChar(DecryptedDataBuffer[1], length(DecryptedDataBuffer), 0);
         DecryptedDataBuffer := '';
      end;
   end;

end;

procedure TLicenceKeyChecking.LoadFromStream(FIn: TStream);
label lExit;
var md5Calculer: TMd5HashInfo;
    md5In: TMd5HashInfo;
    iCheck: integer;

    iLeave       : integer;
    tmpStream    : TMemoryStream;
    sMd5Crypted  : string;
    sMd5Decrypted: string;

    DeCompressionStream: TZDecompressionStream;

    InDecompressed: TMemoryStream;
begin

    FIsValidFile := false;
    iLeave := 0;

    FHash := 354;

    if FIn = nil then begin
       exit;
    end;



    // décompresse
    InDecompressed := TMemoryStream.Create;


    FIn.Read(iCheck, SizeOf(Integer));

    if (iCheck and 1) = 1 then begin
       // nombre impair
       exit;
    end;

    if FIn.Size < 32 then begin
       exit;
    end;

    try
       DeCompressionStream := TZDecompressionStream.Create(FIn);
       InDecompressed.CopyFrom(DecompressionStream, FIn.Size - SizeOf(Integer));
       DecompressionStream.Free;
       DecompressionStream := nil;

       FIn.Seek(0, soBeginning);

       InDecompressed.Seek(0, soBeginning);
    except
       iLeave := 1;
    end;

    if iLeave = 1 then begin
       if DecompressionStream <> nil then begin
          DecompressionStream.Free;
       end;
       InDecompressed.Free;
       exit;
    end;

    if InDecompressed.Read(FHeader, SizeOf(FHeader)) < SizeOf(FHeader) then begin
       InDecompressed.Free;
       exit;
    end;

    // recommence la décompression avec la bonne taille

    FIn.Position := SizeOf(Integer);
    InDecompressed.Free;
    InDecompressed := TMemoryStream.Create;

    try
       DeCompressionStream := TZDecompressionStream.Create(FIn);
       InDecompressed.CopyFrom(DecompressionStream, FHeader.TotalSize);
       DecompressionStream.Free;
       DecompressionStream := nil;

       FIn.Seek(0, soBeginning);

       InDecompressed.Seek(0, soBeginning);
    except
       iLeave := 1;
    end;

    if iLeave = 1 then begin
       if DecompressionStream <> nil then begin
          DecompressionStream.Free;
       end;
       InDecompressed.Free;
       exit;
    end;


    // Relit l'header
    if InDecompressed.Read(FHeader, SizeOf(FHeader)) < SizeOf(FHeader) then begin
       InDecompressed.Free;
       exit;
    end;

    if InDecompressed.Size <> FHeader.TotalSize then begin
       InDecompressed.Free;
       exit;
    end;



    md5Calculer := md5Stream(InDecompressed, FHeader.File1Size);

    // Le champ ImmediateLeave fait quitter tout de suite
    // mais on laisse faire le calcul md5 avant
    if FHeader.ImmediateLeave > 0 then begin
lExit:
       InDecompressed.Free;
       exit;
    end;


    tmpStream := TMemoryStream.Create;

    // Lit le hash md5 crypté
    tmpStream.CopyFrom(InDecompressed, FHeader.FileMd5Size);
    tmpStream.Seek(0, soBeginning);

    sMd5Crypted   := iaEncode64s(tmpStream);
    tmpStream.Free;
    tmpStream := nil;

    // Decrypte le md5 (mais sera sous forme binaire dans le string)
    sMd5Decrypted := iaRSADecryptageKeyStream(sMd5Crypted, RSA_GetPublicKey, CNT_PUBLICKEY_PASSPHRASE);

    if length(sMd5Decrypted) < SizeOf(md5In) then begin
       InDecompressed.Free;
       exit;
    end;

    Move(sMd5Decrypted[1], md5In, SizeOf(md5In));

    FHash := md5Somme(md5Calculer);

    if md5IsEgal(md5In, md5Calculer) = false then begin
       // Definit un nombre impair
       FHeader.ImmediateLeave := (iCheck + md5Somme(md5In)) or 1;

       // L'écrit dans le debut du fichier
       FIn.Write(FHeader.ImmediateLeave, SizeOf(FHeader.ImmediateLeave));
       FIn.Seek(64, soBeginning);
    end;


    
    FDataCrypted := iaEncode64s(InDecompressed);

    if FHeader.ImmediateLeave > 0 then begin
       InDecompressed.Free;
       exit;
    end;

    if length(FDataCrypted) < 64 then begin
       goto lExit;
    end;

    
    FIsValidFile := true;
    InDecompressed.Free;

end;


procedure TLicenceKeyChecking.LoadFromFile(Fichier: string);
var FIn: TFileStream;
begin

    FIsValidFile := false;

    if FileExists(Fichier) = false then begin
       exit;
    end;

    try
       FIn := TFileStream.Create(Fichier, fmOpenReadWrite);
    except
       FIn := nil;
    end;

    if FIn = nil then begin
       exit;
    end;

    LoadFromStream(FIn);

    FIn.Free;
end;
{$ENDIF}



function RSA_GetPublicKey: TStream;
begin
    RSA_LoadPublicKey;
    Result := RSA_PublicKey;
end;

procedure RSA_LoadPublicKey;
begin
    if RSA_PublicKey = nil then begin
       RSA_PublicKey := TResourceStream.Create(Hinstance, PUBLIC_KEY_RESSOURCE_NAME, RT_RCDATA);
    end;

    RSA_PublicKey.Seek(0, soBeginning);

end;

procedure RSA_UnLoadPublicKey;
begin
    if RSA_PublicKey <> nil then begin
       RSA_PublicKey.Free;
       RSA_PublicKey := nil;
    end;

end;


initialization
    RSA_PublicKey := nil;


finalization
    RSA_UnLoadPublicKey;

end.
